<?php
/**
 * Mysql.php
 * 08:58:27
 * @author qqinxl
 * @package models_db
 * LICENSE 
 * 本类是系统读取数据库数据的重要类。支持多重数据库联合，条件联合等。
 * $sqlArray['tablename'] = 'users' 该项目必须存在
 * $sqlArray['select'] = array('username','email'); 
 * 如果不设置，默认是全部字段 即$sqlArray['select'] = '*'
 * 或者$sqlArray['select'] = array('username','LOWER(email)'));
 * 或者$sqlArray['select'] = array('username','a_new_name' => 'users.price*2'));
 * 条件支持多位数组；但是建议使用 $sqlArray['where']['users.sakura = ?'] = 1; 这种格式
 * 如果少掉一个'?' 例$sqlArray['where']['users.type = '] = 2;也可以
 * 或者$sqlArray['where'][0] = 'userinfo.gender = 1';
 * $sqlArray['where'][2] = 'userinfo.rank = 4';
 * 排序也可以自由组合。
 * $sqlArray['order'][0] = 'users.userid';
 * $sqlArray['order'][1] = 'userinfo.type';
 * 或者$sqlArray['order'] = 'users.userid DESC';
 * Table联合，只要存在连接条件
 * $sqlArray['join'][0]['type'] = 'leftjoin'; 可以不存在;默认是join 支持leftjoin rightjoin
 * $sqlArray['join'][0]['tablename'] = 'userinfo' ;必须存在
 * $sqlArray['join'][0]['on'] = 'userinfo.userid = users.userid'; 必须存在
 * $sqlArray['join'][0]['select'] = '*';意思是全取数据； 必须存在
 * $sqlArray['join'][0]['select'] = null;这种情况，不取该表格数据。或者不在select
 * 允许再连接一个数据库，必须存在连接条件
 * $sqlArray['join'][1]['type'] = 'join';
 * $sqlArray['join'][1]['tablename'] = 'userview' ;
 * $sqlArray['join'][1]['on'] = 'userview.userid = users.userid';
 * $sqlArray['join'][1]['select'] = 'array('userviewid','userviewtime')'; *
 * 如果只有一个连接的数据库 可以写成:
 * $sqlArray['join']['type'] = 'join';
 * $sqlArray['join']['tablename'] = 'userview' ;
 * $sqlArray['join']['on'] = 'userview.userid = users.userid';
 * $sqlArray['join']['select'] = 'array('userviewid','userviewtime')';
 * @copyright  Copyright (c) lin-international
*/
/**
 * @package models_db
 */
class DbOperation_Select {
	/**
	  * 
	  * @var Zend_Db_Adapter_Abstract
	*/
	private $_db = null;
	/**
	  * 
	  * @var Zend_Db_Select
	*/
	private $_select = null;
	/**
     * Singleton instance
     * @var DbOperation_Select
     */
	protected static $_instance = null;

	/**
     * Constructor
     * @return void
     */
	private function __construct(){
		if(empty($this->_db)){
			try{
				$this->_db = Zend_Db_Table::getDefaultAdapter();
			}catch(Exception $e){
				throw $e;
			}
			if(empty($this->_db)){
				throw new Exception('dbAdapter is a wrong value.');
			}
			if($this->_db instanceof  Zend_Db_Adapter_Abstract){
			}else{
				throw new Exception('dbAdapter is a wrong value.');
			}
		}
	}
	/**
     * Singleton instance 
     * @return DbOperation_Select
     */
	public static function getInstance(){
		if (null === self::$_instance) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}
	/**
     * 使用count(*)查询计算某一条件下的数据总量。
     * 变量$spelCounts是因为Sql语句似乎只支持一个COUNT计算.
     * 比如通过group by user之后我们得出每个用户的邮件数（这里利用一次count），然后再计算有多少这种用户。
     * 这种计算没有找到一个完整的sql直接得出。也就是无法count两次。
     * 一种解决办法是先select出来，然后再对select出来的表格进行操作；这样sql过于复杂，不采纳。
     * 另外一种解决办法，就是得出结果，再程序里，我们来count总量。此时$spelCounts为true
     * @param array $sqlArray
     * @param bool $spelCounts 
     * @param bool $debug
     * @throws Exception
     * @return int 
 	*/

	public function getCounts($sqlArray,$spelCounts = false){
		try{
			$this->getSql($sqlArray,null,null,true);
			$row = $this->_db->fetchAll($this->_select);			
		}catch (Exception $e){
			throw $e;
		}
		if(empty($row)){
			return null;
		}
		if($spelCounts){
			return count($row);
		}
		if(isset($row[1])){
			return count($row);
		}else{
			return $row[0]['counts'];
		}
	}
	
	/**
     * 获取某一查询条件下的数据$sqlAray.如果第四个参数不存在,以数组形式返回;否则按要求封装成类.
     * @param array $sqlArray
     * @param int $limit
     * @param int $offset
     * @param string $className 封装为该类，否则以数组形式返回
     * @param bool $debug
     * @throws Exception
     * @return int 
 */
	public function getData($sqlArray,$limit = null,$offset = null,$className = null){
		try{
			if(is_array($sqlArray) or is_string($sqlArray)){
				$sql = $this->getSql($sqlArray,$limit,$offset);
			}else{
				return null;
			}
			$row = $this->_db->fetchAll($sql);			
		}catch (Exception $e){
			throw $e;
		}
		if(empty($row) or !is_array($row)){
			return null;
		}
		if(empty($className)){
			return $row;
		}else{
			$object = new $className();
			if($object instanceof Object_Abstract ){
				foreach ($row as $key=>$value){
					$objects[$key] = new $className();
					$objects[$key]->_row = $value;
				}
				return $objects;
			}else{
				return $row;
			}
		}
	}
	/**
     * 获取某一查询条件下的数据$sqlAray转化的sql语句。这是该类的基础函数。
     * @param array $sqlArray
     * @param int $limit
     * @param int $offset
     * @param bool $isSpecial 是否要只是计算总数量。
     * @throws Exception
     * @return string 
 */
	function getSql($sqlArray,$limit = null,$offset = null,$isSpecial = false){
		try{
			if(empty($sqlArray)){
				throw new Exception('sqlArray is a wrong value.');
			}
			if(is_array($sqlArray)){
				if(empty($sqlArray['tablename'])){
					throw new Exception('The value of tablename in sqlArray must exist.');
				}
				if(!is_bool($isSpecial)){
					$isSpecial = false;
				}
				$this->_select = $this->_db->select();
				//分析基础表格
				if(!$isSpecial){
					if(!isset($sqlArray['select'])){
						$this->_select->from($sqlArray['tablename']);
					}elseif(!empty($sqlArray['select']) and is_array($sqlArray['select'])){
						$this->_select->from($sqlArray['tablename'],$sqlArray['select']);
					}elseif(empty($sqlArray['select']) and is_array($sqlArray['select'])){
						$this->_select->from($sqlArray['tablename'],array());
					}else{
						$this->_select->from($sqlArray['tablename']);
					}
				}else{
					$this->_select->from($sqlArray['tablename'],array('counts'=>'count(*)'));
				}
				//分析条件where
				if(isset($sqlArray['where'])){
					$whereArray = $sqlArray['where'];
					if(!empty($whereArray)){
						$this->_sqlWhere($whereArray);
					}
				}
				//分析order。支持数组或者单个order条件
				$orderArray = null;
				if(isset($sqlArray['order'])){
					$orderArray = $sqlArray['order'];
				}elseif(isset($sqlArray['orderby'])){
					$orderArray = $sqlArray['orderby'];
				}
				if(!empty($orderArray)){
					$this->_select->order($orderArray);
				}

				$offset = (int)$offset;
				if(empty($offset)){
					$offset = null;
				}
				$limit = (int)$limit;
				if(empty($limit)){
					$limit = null;
				}
				$this->_select->limit($limit,$offset);
				//多重数组join
				if(isset($sqlArray['join'])){
					$joinArray = $sqlArray['join'];
					if(!empty($joinArray) and is_array($joinArray) ){
						//join一个还是一群Table
						if(array_key_exists('tablename',$joinArray)){
							$this->_sqlJoin($joinArray,$isSpecial);
						}else{
							$this->_sqlJoins($joinArray,$isSpecial);
						}
					}
				}
				//分析group条件
				$groupString = null;
				if(isset($sqlArray['group'])){
					$groupString = $sqlArray['group'];
				}elseif(isset($sqlArray['groupby'])){
					$groupString = $sqlArray['groupby'];
				}
				//groupBy 只能有一个
				if(!empty($groupString) ){
					$this->_select->group($groupString);
				}
				return $this->_select->__toString();
			}elseif(is_string($sqlArray)){
				$limit = (int)$limit;
				if(!empty($limit)){
					$sqlArray .= ' LIMIT '.$limit;
				}
				$offset = (int)$offset;
				if(!empty($offset)){
					$sqlArray .= ' OFFSET '.$offset;
				}
				$result = $sqlArray;
			}else{
				throw new Exception('sqlArray is a wrong value.');
			}
		}catch (Exception $e){
			throw $e;
		}
		return $result;
	}
	/**
	     * 联合多重Table
	     * @param array $joinArray
	     * @param bool $isSpecial 是否要只是计算总数量
	     * @throws Exception
	     * @return void
	 */
	protected function _sqlJoins($joinArray,$isSpecial = false){
		if($this->_select instanceof  Zend_Db_Select){
			foreach ($joinArray as $key=>$value){
				if(is_int($key)){
					if(!empty($value) and is_array($value)){
						$this->_sqlJoin($value,$isSpecial);
					}else{
						throw new Exception('Error value of joins in sqlArray.');
					}
				}else{
					throw new Exception('Error value of joins in sqlArray.');
				}
			}
		}else{
			throw new Exception('Select is a wrong value.');
		}
	}
	/**
	     * join一个表格
	     * @param array $joinArray
	     * @param bool $isSpecial 是否要只是计算总数量
	     * @throws Exception
	     * @return void
	 */
	protected function _sqlJoin($joinArray,$isSpecial = false){
		if($this->_select instanceof  Zend_Db_Select){
			if(!is_array($joinArray) or empty($joinArray)){
				throw new Exception('JoinArray is a wrong value.');
			}
			if(empty($joinArray['on']) or empty($joinArray['tablename'])){
				throw new Exception('Error value of join in sqlArray.');
			}
			if(empty($joinArray['type'])){
				$jointype = 'joinLeft';
			}else{
				$jointype = $joinArray['type'];
			}

			$_joinArrayOn = null;
			if(is_array($joinArray['on'])){
				foreach ($joinArray['on'] as $_on){
					if(empty($_joinArrayOn)){
						$_joinArrayOn = $_on;
					}else{
						$_joinArrayOn .= ' AND '.$_on;
					}
				}
			}else{
				$_joinArrayOn = $joinArray['on'];
			}
			$tablename = $joinArray['tablename'];
			if($isSpecial){
				//如果计算总量就忽略原有的select设置
				$this->_select->$jointype($tablename, $_joinArrayOn,array());
			}else{
				if(!isset($joinArray['select'])or empty($joinArray['select'])){
					$this->_select->$jointype($tablename, $_joinArrayOn,array());
				}elseif($joinArray['select'] == '*'){
					//join表格的主键不能再是id
					$tableinfo = array_keys(Config_Table::getFields($tablename));
					unset($tableinfo[0]);					
					$tableinfo[$tablename.'_id'] ='id';
					$this->_select->$jointype($tablename, $_joinArrayOn,$tableinfo);
				}else{
					$this->_select->$jointype($tablename, $_joinArrayOn,$joinArray['select']);
				}
			}
		}else{
			throw new Exception('Select is a wrong value.');
		}
	}
	/**
     * 分析where条件。
     * 所谓的两种形式的数组，就是按键值区别的
     * $whereArray['userid = ?'] = 1;
     * $whereArray[0]='userid = 1';
     * @param array $whereArray
     * @throws Exception
     * @return void
	 */
	protected function _sqlWhere($whereArray){
		if($this->_select instanceof  Zend_Db_Select){
			if(!empty($whereArray) and is_array($whereArray)){
				foreach($whereArray as $key=>$value){

					if(!is_int($key)){
						$key = trim($key);
						if(strpos($key,'=') === false and strpos($key,'>') === false and strpos($key,'<') === false){
							$key .=' = ';
						}
						if(substr($key, -1) !='?'){
							$key .=' ?';
						}
						$this->_select->where($key,$value);
					}else{
						$this->_select->where($value);
					}
				}
			}else{
				throw new Exception('whereArray is a wrong value.');
			}
		}else{
			throw new Exception('Select is a wrong value.');
		}
	}	
}